La Odisea

Comencemos cargando los paquetes iniciales

library("dplyr")
library("tm.plugin.webmining")
library("purrr")
library("tidytext")
library("gutenbergr")
library("ggplot2")

Elección del Texto

En esta ocasión estaremos usando el paquete gutenbergr que nos permite descargar los libros del Proyecto Gutenberg. Para obtener el ID del libro podemos ingresar a la página del Proyecto Gutenberg desarrollado por David Robinson

Obtenemos el ID del libro

gutenberg_metadata %>% filter(title == "The Odyssey")

Descargamos el libro con el ID

odisea <- gutenberg_download(3160)
# Echemos un vistazo al contenido
odisea

Limpieza de Datos

Separamos las líneas de texto en palabras y quitamos las palabras vacías

odisea_ordenada <- odisea %>%
  unnest_tokens(word, text) %>%
  anti_join(stop_words)
odisea_ordenada

Gráfica de Palabras Frecuentes

#filtramos palabras con 3 o menos letras
odisea_ordenada %>%
  count(word, sort = TRUE) %>%
  filter(n > 135,
         !nchar(as.character(word)) <= 3) %>%
  mutate(word = reorder(word, n)) %>%
  ggplot(aes(word, n), color = n) +
  geom_col() +
  xlab(NULL) +
  coord_flip() + 
    labs(title = "Las palabras más frecuentes en La Odisea",
         y = "Cantidad de palabras")

Tabla de palabras Frecuentes

Mostramos la frecuencia de aparición de cada palabra en el libro

odisea_ordenada %>%
  count(word, sort = TRUE)

Obtención de Sentimientos

Existen algunos diccionarios que podemos usar en R para obtener sentimientos de palabras. En esta sección debemos elegir el método que se adapte mejor a nuestro texto objetivo y tenga relación con lo que queremos obtener de él.

Método Bing

Para eso necesitamos observar qué hacen exactamente cada uno de ellos.

Tabla de Sentimientos

Este método puede clasificar 1620 palabras del libro entre sentimiento “positivo” y “negativo”

sentimientos_bing <- odisea_ordenada %>%
  inner_join(get_sentiments("bing")) %>%
  count(word, sentiment, sort = TRUE) %>%
  ungroup()
sentimientos_bing

Gráfica de Sentimientos

sentimientos_bing %>%
  group_by(sentiment) %>%
  top_n(10) %>%
  ungroup() %>%
  mutate(word = reorder(word, n)) %>%
  ggplot(aes(word, n, fill = sentiment)) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~sentiment, scales = "free_y") +
  labs(y = "Contribución de sentimiento por palabra",
       x = NULL) +
  coord_flip()

Método NRC

library(textdata)
odiseanrc <- odisea_ordenada %>%
  inner_join(get_sentiments("nrc"))
odiseanrc
odiseanrc1 <- odiseanrc %>%
    count(word, sentiment, sort = TRUE) %>%
    ungroup()
odiseanrc1
odiseanrc1 %>%
  group_by(sentiment) %>%
  top_n(10) %>%
  ungroup() %>%
  mutate(word = reorder(word, n)) %>%
  ggplot(aes(word, n, fill = sentiment)) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~sentiment, scales = "free_y") +
  labs(y = "Contribución de sentimiento por palabra",
       x = NULL) +
  coord_flip()

odiseaafinn <- odisea_ordenada %>%
  inner_join(get_sentiments("afinn"))
odiseaafinn
valorafinn <- as.numeric(odiseaafinn$value)
suavizado_afinn <- tibble(Palabras = 1:length(valorafinn), 
       Sentimiento = scales::rescale(valorafinn)) %>% 
    ggplot(aes(Palabras, Sentimiento)) +
    geom_smooth(se = FALSE) +
    theme_bw() +
    ggtitle("suavizado de tendencia GAM")
suavizado_afinn

library(syuzhet)
simple_plot(valorafinn)

library(wordcloud)
odisea_ordenada %>%
  anti_join(stop_words) %>%
  count(word) %>%
  with(wordcloud(word, n, max.words = 100))

library(reshape2)
odisea_ordenada %>%
  inner_join(get_sentiments("bing")) %>%
  count(word, sentiment, sort = TRUE) %>%
  acast(word ~ sentiment, value.var = "n", fill = 0) %>% as.data.frame() %>% 
    rename(Negativas = negative,
                                                                Positivas = positive) %>%
  comparison.cloud(colors = c("#F8766D", "#00BFC4"),
                   max.words = 100)

odisea_bigramas <- odisea %>%
  unnest_tokens(bigram, text, token = "ngrams", n = 2) %>% rename(bigrama = bigram)
odisea_bigramas
library(tidyr)
bigramas_separados <- odisea_bigramas %>%
  separate(bigrama, c("palabra1", "palabra2"), sep = " ")
bigramas_filtrados <- bigramas_separados %>%
  filter(!palabra1 %in% stop_words$word) %>%
  filter(!palabra2 %in% stop_words$word)
conteo_bigramas <- bigramas_filtrados %>% 
  count(palabra1, palabra2, sort = TRUE)
conteo_bigramas
bigramas_unidos <- bigramas_filtrados %>%
  unite(bigrama, palabra1, palabra2, sep = " ")
bigramas_unidos

Bigramas

Tabla

Mostramos el conteo de los bigramas

library(viridis)
conteo_bigramas_unidos <- bigramas_unidos %>%
  count(bigrama,sort = T)
conteo_bigramas_unidos <- conteo_bigramas_unidos %>% filter(n>10)
conteo_bigramas_unidos

Tabla relacional

Relaciones entre bigramas

library(igraph)
grafica_bigrama <- conteo_bigramas %>%
  filter(n > 8) %>%
  graph_from_data_frame()
grafica_bigrama
## IGRAPH 109b853 DN-- 37 26 -- 
## + attr: name (v/c), n (e/n)
## + edges from 109b853 (vertex names):
##  [1] thy     ->soul    thou    ->art     native  ->shore   suitor  ->train  
##  [5] thy     ->care    native  ->land    thy     ->native  thy     ->son    
##  [9] stranger->guest   thy     ->mind    hast    ->thou    martial ->maid   
## [13] menial  ->train   shalt   ->thou    ye      ->peers   art     ->thou   
## [17] train   ->attends blue    ->eyed    bridal  ->day     bridal  ->hour   
## [21] grace   ->divine  homeric ->poems   native  ->shores  powers  ->divine 
## [25] rosy    ->wine    safe    ->return

Gráfica

Mostramos la gráfica de bigramas con más de 10 apariciones

ggplot(conteo_bigramas_unidos,
       aes(reorder(bigrama, n),n,fill = reorder(bigrama,-n))) +
  geom_bar(stat = 'identity') +
  ggtitle("Los bigramas más comunes en La Odisea") +
  coord_flip() +
  theme(panel.grid.minor = element_blank()) +
  xlab(element_blank()) +
  ylab(element_blank()) +
  scale_fill_viridis(discrete = TRUE, option = "D") +
  theme(legend.position = "none")

Gráfica relacional

Mapeo de las relaciones entre bigramas

library(ggraph)
set.seed(2020)
ggraph(grafica_bigrama, layout = "fr") +
  geom_edge_link() +
  geom_node_point(color = "#00BFC4", size = 3) +
  geom_node_text(aes(label = name), vjust = 1.8) +
  ggtitle("Bigramas Comunes en La Odisea")

Odisea por emociones

library(readr)
library(stringr)
odisea_sucia <- read_lines("odisea.txt",
                           skip = 30,
                           n_max = 15500) # Cargamos una versión del libro en formato ".txt"
odisea_limpia <- character()  # Creamos un vector de caracteres
for (i in seq(345, length(odisea_sucia),by = 1)) {  # Desde el título "BOOK I"
        if (i%%10 == 1) # Si el remanente de la división es igual a 1 prosigue
          odisea_limpia[ceiling(i/10)] <- str_c(odisea_sucia[i],
                                                odisea_sucia[i+1],
                                                odisea_sucia[i+2],
                                                odisea_sucia[i+3],
                                                odisea_sucia[i+4],
                                                odisea_sucia[i+5],
                                                odisea_sucia[i+6],
                                                odisea_sucia[i+7],
                                                odisea_sucia[i+8],
                                                odisea_sucia[i+9], sep = " ")
}

odisea_limpia[runif(1,min = 1, max = length(odisea_limpia))]
## [1] "ship with lightning and sunk it in mid ocean, so that all his crew were drowned, while he himself was driven by wind and waves on to my island. I got fond of him and cherished him, and had set my heart on making him immortal, so that he should never grow old all his days; still I cannot cross Jove, nor bring his counsels to nothing; therefore, if he insists upon it, let the man go beyond the seas again; but I cannot send him anywhere myself for I have neither ships nor men who can take him. Nevertheless I will readily give him such advice, in all good faith, as will be likely to bring him safely to his own country.” "
library(syuzhet)
odisea_limpia_nrc <- cbind(linea = seq_along(odisea_limpia), get_nrc_sentiment(odisea_limpia))

Cambiamos los nombres de los sentimientos obtenidos por el léxico

names(odisea_limpia_nrc) <- c("linea","enfado","anticipación","asco","miedo","alegría","tristeza","sorpresa","confianza","negativo","positivo")

Daremos un formato a nuestro vector de sentimientos

# Añadimos el signo negativo a los valores
odisea_limpia_nrc$negativo <- -odisea_limpia_nrc$negativo
# Combinamos las columnas positivo y negativo para crear el "valor" de ambos sentimientos en una sola
nega_posi <- odisea_limpia_nrc %>%
  select(linea, positivo, negativo) %>% 
  melt(id = "linea")
# Renombramos las columnas
names(nega_posi) <- c("linea", "sentimiento", "valor")
library(ggthemes)
ggplot(data = nega_posi, aes(x = linea, y = valor, fill = sentimiento)) +
        geom_bar(stat = 'identity', position = position_dodge()) + theme_minimal() +
        ylab(element_blank()) + 
    xlab("Número de párrafo") +
        ggtitle("Sentimientos Positivos y Negativos en La Odisea")

tibble(palabras = 1:length(nega_posi$valor), 
       Sentimiento = rescale(nega_posi$valor)) %>% 
    ggplot(aes(palabras, Sentimiento)) +
    geom_smooth(se = TRUE,
                color = "#00BFC4",
                fill = "#00BFC4",
                size = 1) +
    theme(plot.title = element_text(family = "Helvetica", face = "bold", size = (15))) +
    ggtitle("Tendencia suavizada")

emociones <- odisea_limpia_nrc %>% select(linea, enfado, anticipación, asco, miedo, alegría, tristeza, sorpresa, confianza) %>% melt(id = "linea")
names(emociones) <- c("número de línea", "sentimiento", "valor")
grupo_emociones <- group_by(emociones, sentimiento)
por_emociones <- summarise(grupo_emociones, 
                         valores=sum(valor))
ggplot(aes(reorder(x=sentimiento, valores), y=valores, fill=reorder(sentimiento,-valores)), data = por_emociones) +
  geom_bar(stat = 'identity') + ggtitle("Sentimientos en La Odisea") +
    xlab(NULL) +
  coord_flip() + theme(legend.position="none") +
  scale_fill_viridis(discrete = TRUE, option = "D")